﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QLabValues;
using AtomicTypes;
using NumericalCalculations;

namespace NumericalCalculations
{
    public partial class Functions
    {

        private bool compareNCharsInTwoQCharNDimArrays(QCharNDimArray firstArray, QCharNDimArray secondArray, uint n, bool caseInsensitive)
        {
            if (firstArray.Data.Count() < n || secondArray.Data.Count() < n)
            {
                return false;
            }

            for (int i = 0; i < n; i++)
            {
                char first = firstArray.Data[i].Real;
                char second = secondArray.Data[i].Real;

                if ((caseInsensitive && char.ToUpperInvariant(first) != char.ToUpperInvariant(second)) || (!caseInsensitive && first != second))
                {
                    return false;
                }
            }

            return true;
        }

        private List<IQLabValue> compareStrings(List<IQLabValue> args, bool caseInsenstive)
        {
            string functionName = "strncmp";

            if (caseInsenstive)
            {
                functionName += "i";
            }

            if (args.Count != 3)
            {
                throw new NumericalCalculationsException("??? Error using ==> " + functionName + " \nFunction has three arguments.");
            }

            if (!(Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QCharNDimArray) || Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QCell)) ||
                !(Helper.canConvertToEnum(args.ElementAt(1), IQLabValueType.QCharNDimArray) || Helper.canConvertToEnum(args.ElementAt(1), IQLabValueType.QCell)))
            {
                throw new NumericalCalculationsException("??? Error using ==> " + functionName + " \nFirst and second argument must be a string or string array.");
            }

            if (!Helper.canConvertToEnum(args.ElementAt(2), IQLabValueType.QFloat64NDimArray) && Helper.checkIfScalar(args.ElementAt(2)))
            {
                throw new NumericalCalculationsException("??? Error using ==> " + functionName + " \nThird argument must be a number.");
            }

            // Get n from the arguments
            QFloat64NDimArray dimArray = new QFloat64NDimArray(args.ElementAt(2));
            uint n = (uint)dimArray[0, 0].Real;

            // Result
            List<IQLabValue> result = new List<IQLabValue>();

            if (Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QCharNDimArray) && Helper.canConvertToEnum(args.ElementAt(1), IQLabValueType.QCharNDimArray))
            {
                QCharNDimArray firstStringArray = new QCharNDimArray(args.ElementAt(0));
                QCharNDimArray secondStringArray = new QCharNDimArray(args.ElementAt(1));

                bool equal = this.compareNCharsInTwoQCharNDimArrays(firstStringArray, secondStringArray, n, caseInsenstive);

                result.Add(new QBooleanNDimArray(new QBoolean[] { new QBoolean(equal) }, new int[] { 1, 1 }));
            }
            else if (Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QCell) && Helper.canConvertToEnum(args.ElementAt(1), IQLabValueType.QCell))
            {
                QCell firstCellArray = new QCell(args.ElementAt(0));
                QCell secondCellArray = new QCell(args.ElementAt(1));

                QBooleanNDimArray resultArray = new QBooleanNDimArray(firstCellArray.DimensionSize);
                for (int i = 0; i < firstCellArray.Data.Count(); i++)
                {
                    bool equal = false;
                    if (Helper.canConvertToEnum(firstCellArray.Data[i], IQLabValueType.QCharNDimArray) &&
                        Helper.canConvertToEnum(secondCellArray.Data[i], IQLabValueType.QCharNDimArray))
                    {
                        resultArray.SetDataElement(i, new QBoolean(this.compareNCharsInTwoQCharNDimArrays
                            ((QCharNDimArray)firstCellArray.Data[i], (QCharNDimArray)secondCellArray.Data[i], n, caseInsenstive)));
                    }
                    else
                    {
                        resultArray.SetDataElement(i, new QBoolean(false));
                    }
                }
            }
            else
            {
                QCell cellArray = null;
                QCharNDimArray charArray = null;
                if (Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QCell))
                {
                    cellArray = new QCell(args.ElementAt(0));
                    charArray = new QCharNDimArray(args.ElementAt(1));
                }
                else
                {
                    cellArray = new QCell(args.ElementAt(1));
                    charArray = new QCharNDimArray(args.ElementAt(0));
                }

                QBooleanNDimArray resultArray = new QBooleanNDimArray(cellArray.DimensionSize);
                for (int i = 0; i < cellArray.Data.Count(); i++)
                {
                    if (Helper.canConvertToEnum(cellArray.Data[i], IQLabValueType.QCharNDimArray))
                    {
                        resultArray.SetDataElement(i, new QBoolean(this.compareNCharsInTwoQCharNDimArrays((QCharNDimArray)cellArray.Data[i], charArray, n, caseInsenstive)));
                    }
                    else
                    {
                        resultArray.SetDataElement(i, new QBoolean(false));
                    }
                }
            }

            return result;
        }

        public List<IQLabValue> strncmp(List<IQLabValue> args)
        {
            return this.compareStrings(args, false);
        }

        public List<IQLabValue> strncmpi(List<IQLabValue> args)
        {
            return this.compareStrings(args, true);
        }

        /// <summary>
        /// Length of vector or largest array dimension.
        /// </summary>
        /// <param name="args">List of arguments.</param>
        /// <returns>List with that has larget array dimension.</returns>
        public List<IQLabValue> length(List<IQLabValue> args)
        {
            if (args.Count != 1)
            {
                throw (new NumericalCalculationsException("??? Error using ==> length \nOnly one argument is needed."));
            }

            if (!Helper.canConvertListToEnum(args, IQLabValueType.QFloat64NDimArray))
            {
                throw (new NumericalCalculationsException("??? Error using ==> length \nArgument is not valid."));
            }

            List<IQLabValue> result = new List<IQLabValue>();
            QFloat64NDimArray array = new QFloat64NDimArray(args.ElementAt(0));

            ulong size = 0;

            for (uint i = 0; i < array.DimensionSize.Count(); i++)
            {
                if ((ulong)array.DimensionSize[i] > size)
                {
                    size = (ulong)array.DimensionSize[i];
                }
            }

            result.Add(new QUInt64NDimArray(new QUInt64[] { new QUInt64(size) }, new int[] { 1, 1 }));
            return result;
        }

        public List<IQLabValue> nonzeros(List<IQLabValue> args)
        {
            if (args.Count != 1)
            {
                throw (new NumericalCalculationsException("??? Error using ==> nonzeros \nOnly one argument is needed."));
            }

            if (!Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QFloat64NDimArray))
            {
                throw new NumericalCalculationsException("??? Error using ==> nonzeros \nArgument is not in proper format!");
            }

            QFloat64NDimArray inputArray = new QFloat64NDimArray(args.ElementAt(0));

            List<QFloat64> resultData = new List<QFloat64>();

            for (int i = 0; i < inputArray.Data.Count(); i++)
            {

                QFloat64 value = inputArray.Data[i];

                if (value.Real != 0 || value.Imag != 0)
                {
                    resultData.Add(new QFloat64(value));
                }
            }


            QFloat64NDimArray resultArray = new QFloat64NDimArray(resultData.ToArray(), new int[] { 1, resultData.Count });
            List<IQLabValue> result = new List<IQLabValue>();
            result.Add(resultArray);

            return result;
        }

        public List<IQLabValue> numel(List<IQLabValue> args)
        {
            if (args.Count > 2 || args.Count == 0)
            {
                throw new NumericalCalculationsException("??? Error using ==> numel \nOne or two arguments are needed.");
            }

            QFloat64NDimArray resultArray = new QFloat64NDimArray(new int[] { 1, 1 });
            if (args.Count == 1)
            {


                QFloat64NDimArray input = new QFloat64NDimArray(args.ElementAt(0));
                resultArray.SetDataElement(0, new QFloat64(input.Data.Count()));
            }
            else
            {

                if (!Helper.canConvertToEnum(args.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                {
                    throw new NumericalCalculationsException("??? Error using ==> numel \nSecond argument is not in proper format");
                }

                QFloat64NDimArray inputArray = new QFloat64NDimArray(args.ElementAt(0));
                QFloat64NDimArray indexArray = new QFloat64NDimArray(args.ElementAt(1));

                int size = inputArray.Data.Count();

                for (int i = 0; i < indexArray.Data.Count(); i++)
                {
                    size /= inputArray.DimensionSize[i];
                }

                resultArray.SetDataElement(0, new QFloat64(size));
            }

            List<IQLabValue> resultList = new List<IQLabValue>();
            resultList.Add(resultArray);
            return resultList;
        }

        public List<IQLabValue> nzmax(List<IQLabValue> args)
        {
            if (args.Count != 1)
            {
                throw new NumericalCalculationsException("??? Error using ==> nzmax \nOnly one argument is allowed!");
            }

            if (!Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QFloat64NDimArray))
            {
                throw new NumericalCalculationsException("??? Error using ==> nzmax \nArgument is not proper type!");
            }

            QFloat64NDimArray input = new QFloat64NDimArray(args.ElementAt(0));

            int size = input.Data.Count();

            foreach (QFloat64 number in input.Data)
            {
                if (number.Real == 0 && number.Imag == 0)
                {
                    size--;
                }
            }

            QFloat64NDimArray resultArray = new QFloat64NDimArray(new QFloat64[] { new QFloat64(size) }, new int[] { 1, 1 });
            List<IQLabValue> result = new List<IQLabValue>();
            result.Add(resultArray);
            return result;
        }

        public List<IQLabValue> rand(List<IQLabValue> o)
        {
            Random r = new Random();
            if (o.Count == 0)
            {
                List<IQLabValue> res = new List<IQLabValue>();
                res.Add(new QFloat64NDimArray(new QFloat64[] { new QFloat64(r.NextDouble()) }, new int[] { 1, 1 }));
                return res;
            }

            if ((o.ElementAt(0).GetEnumType() != IQLabValueType.QFloat64NDimArray) &&
                (o.ElementAt(0).GetEnumType() != IQLabValueType.QInt64NDimArray) &&
                    (o.ElementAt(0).GetEnumType() != IQLabValueType.QUInt64NDimArray))
            {
                throw new NumericalCalculationsException("??? Error using ==> rand \nArguments have inappropriate type.");
            }

            QFloat64NDimArray qf;
            string type = "";

            if (o.Count > 1)
            {
                List<QFloat64> inputData = new List<QFloat64>();

                foreach (var element in o)
                {
                    if (element != o.Last() && (element.GetEnumType() != IQLabValueType.QFloat64NDimArray) &&
                        (element.GetEnumType() != IQLabValueType.QInt64NDimArray) &&
                        (element.GetEnumType() != IQLabValueType.QUInt64NDimArray))
                    {
                        throw new NumericalCalculationsException("??? Error using ==> rand \nArguments have inappropriate type.");
                    }

                    if (element == o.Last() && o.Last().GetEnumType() == IQLabValueType.QCharNDimArray)
                    {
                        continue;
                    }

                    QFloat64NDimArray input = new QFloat64NDimArray(element);

                    foreach (QFloat64 number in input.Data)
                    {
                        inputData.Add(number);
                    }
                }

                if (o.Last().GetEnumType() == IQLabValueType.QCharNDimArray)
                {
                    QCharNDimArray typeArray = new QCharNDimArray(o.Last());
                    foreach (QChar qchar in typeArray.Data)
                    {
                        type += qchar.Real;
                    }
                }


                qf = new QFloat64NDimArray(inputData.ToArray(), new int[] { 1, inputData.Count });
            }
            else
            {
                qf = new QFloat64NDimArray(o.ElementAt(0));
            }

            int[] size = new int[qf.Data.Count()];
            int arraySize = 1;

            for (int i = 0; i < size.Count(); i++)
            {
                size[i] = (int)qf.Data[i].Real;
                arraySize *= size[i];
            }

            if (size.Count() == 1)
            {
                arraySize = size[0] * size[0];
            }

            QFloat64[] resultData = new QFloat64[arraySize];

            for (int i = 0; i < arraySize; i++)
            {
                if (type == "single")
                {
                    resultData[i] = new QFloat64((float)r.NextDouble());
                }
                else
                {
                    resultData[i] = new QFloat64(r.NextDouble());
                }
            }

            QFloat64NDimArray resultArray = new QFloat64NDimArray(resultData, size);

            List<IQLabValue> listResult = new List<IQLabValue>();
            listResult.Add(resultArray);
            return listResult;
        }

        public List<IQLabValue> randi(List<IQLabValue> args)
        {
            Random r = new Random();
            if (args.Count == 0)
            {
                throw new NumericalCalculationsException("??? Error using ==> randi \nrandi has one or more arguments!");
            }

            if ((args.ElementAt(0).GetEnumType() != IQLabValueType.QFloat64NDimArray) &&
                (args.ElementAt(0).GetEnumType() != IQLabValueType.QInt64NDimArray) &&
                    (args.ElementAt(0).GetEnumType() != IQLabValueType.QUInt64NDimArray))
            {
                throw new NumericalCalculationsException("??? Error using ==> randi \nArguments have inappropriate type.");
            }

            QFloat64NDimArray boundriesArray = new QFloat64NDimArray(args.ElementAt(0));
            int imin = 1;
            int imax = 1;

            if (boundriesArray.Data.Count() == 1)
            {
                imax = (int)boundriesArray[0, 0].Real;
            }
            else if (boundriesArray.Data.Count() == 2)
            {
                imin = (int)boundriesArray[0, 0].Real;
                imax = (int)boundriesArray[0, 1].Real;
            }
            else
            {
                throw new NumericalCalculationsException("??? Error using ==> randi \nFirst argument has no valid length");
            }

            QFloat64NDimArray resultArray = null;

            if (args.Count == 1)
            {
                resultArray = new QFloat64NDimArray(new QFloat64[] { new QFloat64(imin + Math.Round(r.NextDouble() * (imax - imin))) }, new int[] { 1, 1 });
            }
            else
            {
                List<QFloat64> inputData = new List<QFloat64>();

                foreach (var element in args)
                {
                    if (element == args.First())
                    {
                        continue;
                    }

                    if ((element.GetEnumType() != IQLabValueType.QFloat64NDimArray) &&
                        (element.GetEnumType() != IQLabValueType.QInt64NDimArray) &&
                        (element.GetEnumType() != IQLabValueType.QUInt64NDimArray))
                    {
                        throw new NumericalCalculationsException("??? Error using ==> rand \nArguments have inappropriate type.");
                    }

                    QFloat64NDimArray input = new QFloat64NDimArray(element);

                    foreach (QFloat64 number in input.Data)
                    {
                        inputData.Add(number);
                    }
                }

                QFloat64NDimArray qf = new QFloat64NDimArray(inputData.ToArray(), new int[] { 1, inputData.Count });

                int[] size = new int[qf.Data.Count()];
                int arraySize = 1;

                for (int i = 0; i < size.Count(); i++)
                {
                    size[i] = (int)qf.Data[i].Real;
                    arraySize *= size[i];
                }

                QFloat64[] resultData = new QFloat64[arraySize];

                for (int i = 0; i < arraySize; i++)
                {
                    resultData[i] = new QFloat64(imin + Math.Round(r.NextDouble() * (imax - imin)));
                }

                resultArray = new QFloat64NDimArray(resultData, size);
            }

            List<IQLabValue> listResult = new List<IQLabValue>();
            listResult.Add(resultArray);
            return listResult;
        }

        private double boxMullerNextDouble(Random r)
        {
            double randStdNormal = Math.Sqrt(-2.0 * Math.Log(r.NextDouble())) *
                         Math.Sin(2.0 * Math.PI * r.NextDouble());
            return randStdNormal;
        }

        public List<IQLabValue> randn(List<IQLabValue> args)
        {
            Random r = new Random();
            if (args.Count == 0)
            {
                List<IQLabValue> res = new List<IQLabValue>();
                res.Add(new QFloat64NDimArray(new QFloat64[] { new QFloat64(boxMullerNextDouble(r)) }, new int[] { 1, 1 }));
                return res;
            }

            if ((args.ElementAt(0).GetEnumType() != IQLabValueType.QFloat64NDimArray) &&
                (args.ElementAt(0).GetEnumType() != IQLabValueType.QInt64NDimArray) &&
                    (args.ElementAt(0).GetEnumType() != IQLabValueType.QUInt64NDimArray))
            {
                throw new NumericalCalculationsException("??? Error using ==> rand \nArguments have inappropriate type.");
            }

            QFloat64NDimArray qf;
            string type = "";

            if (args.Count > 1)
            {
                List<QFloat64> inputData = new List<QFloat64>();

                foreach (var element in args)
                {
                    if (element != args.Last() && (element.GetEnumType() != IQLabValueType.QFloat64NDimArray) &&
                        (element.GetEnumType() != IQLabValueType.QInt64NDimArray) &&
                        (element.GetEnumType() != IQLabValueType.QUInt64NDimArray))
                    {
                        throw new NumericalCalculationsException("??? Error using ==> rand \nArguments have inappropriate type.");
                    }

                    if (element == args.Last() && args.Last().GetEnumType() == IQLabValueType.QCharNDimArray)
                    {
                        continue;
                    }

                    QFloat64NDimArray input = new QFloat64NDimArray(element);

                    foreach (QFloat64 number in input.Data)
                    {
                        inputData.Add(number);
                    }
                }

                if (args.Last().GetEnumType() == IQLabValueType.QCharNDimArray)
                {
                    QCharNDimArray typeArray = new QCharNDimArray(args.Last());
                    foreach (QChar qchar in typeArray.Data)
                    {
                        type += qchar.Real;
                    }
                }


                qf = new QFloat64NDimArray(inputData.ToArray(), new int[] { 1, inputData.Count });
            }
            else
            {
                qf = new QFloat64NDimArray(args.ElementAt(0));
            }

            int[] size = new int[qf.Data.Count()];
            int arraySize = 1;

            for (int i = 0; i < size.Count(); i++)
            {
                size[i] = (int)qf.Data[i].Real;
                arraySize *= size[i];
            }

            if (size.Count() == 1)
            {
                arraySize = size[0] * size[0];
            }

            QFloat64[] resultData = new QFloat64[arraySize];

            for (int i = 0; i < arraySize; i++)
            {
                if (type == "single")
                {
                    resultData[i] = new QFloat64((float)boxMullerNextDouble(r));
                }
                else
                {
                    resultData[i] = new QFloat64(boxMullerNextDouble(r));
                }
            }

            QFloat64NDimArray resultArray = new QFloat64NDimArray(resultData, size);

            List<IQLabValue> listResult = new List<IQLabValue>();
            listResult.Add(resultArray);
            return listResult;
        }

        public List<IQLabValue> isstrprop(List<IQLabValue> args)
        {
            if (args.Count != 2)
            {
                throw new NumericalCalculationsException("??? Error using ==> isstrprop \nTwo arguments are needed!");
            }

            if (!Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QCharNDimArray) || !Helper.canConvertToEnum(args.ElementAt(1), IQLabValueType.QCharNDimArray))
            {
                throw new NumericalCalculationsException("??? Error using ==> isstrprop \n Arguments are strings!");
            }

            QCharNDimArray inputArray = new QCharNDimArray(args.ElementAt(0));
            QCharNDimArray typeArray = new QCharNDimArray(args.ElementAt(1));
            string type = "";
            for (int i = 0; i < typeArray.Data.Count(); i++)
            {
                type += typeArray[0, i].Real;
            }

            QFloat64NDimArray result = new QFloat64NDimArray(inputArray.DimensionSize);

            for (int i = 0; i < inputArray.DimensionSize[0]; i++)
            {
                for (int j = 0; j < inputArray.DimensionSize[1]; j++)
                {
                    char inspectingChar = (char)inputArray[i, j].Real;

                    switch (type)
                    {
                        case "alpha":
                            {
                                if (Char.IsLetter(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);

                                break;
                            }
                        case "alphanum":
                            {
                                if (Char.IsLetterOrDigit(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);

                                break;
                            }
                        case "cntrl":
                            {
                                if (Char.IsControl(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);


                                break;
                            }
                        case "digit":
                            {
                                if (Char.IsDigit(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);

                                break;
                            }
                        case "graphic":
                            {
                                if (Char.IsSeparator(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);

                                break;
                            }
                        case "lower":
                            {
                                if (Char.IsLower(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);

                                break;
                            }
                        case "print":
                            {
                                if (Char.IsWhiteSpace(inspectingChar) || Char.IsLetterOrDigit(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);

                                break;
                            }
                        case "punct":
                            {
                                if (Char.IsPunctuation(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);

                                break;
                            }
                        case "wspace":
                            {
                                if (Char.IsWhiteSpace(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);

                                break;
                            }
                        case "upper":
                            {
                                if (Char.IsUpper(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);

                                break;
                            }
                        case "xdigit":
                            {
                                if (Char.IsWhiteSpace(inspectingChar))
                                    result[i, j] = new QFloat64(1);
                                else
                                    result[i, j] = new QFloat64(0);

                                break;
                            }
                    }
                }
            }

            List<IQLabValue> resultList = new List<IQLabValue>();
            resultList.Add(result);
            return resultList;
        }

        public List<IQLabValue> getenv(List<IQLabValue> args)
        {
            // Check if it argument is string
            if (args.Count != 1)
            {
                throw new NumericalCalculationsException("??? Error using ==> getenv \nOne argument is needed!");
            }

            // Check if it can be converted
            if (!Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QCharNDimArray))
            {
                throw new NumericalCalculationsException("??? Error using ==> getenv \nOne argument is needed!");
            }

            QCharNDimArray input = new QCharNDimArray(args.ElementAt(0));
            string inputString = "";
            for (int i = 0; i < input.Data.Count(); i++)
            {
                inputString += ((QChar)input.Data[i]).Real;
            }

            string env = System.Environment.GetEnvironmentVariable(inputString);

            List<IQLabValue> result = new List<IQLabValue>();

            if (env == null)
            {
                result.Add(new QCharNDimArray(new QChar[] { new QChar('n'), new QChar('u'), new QChar('l'), new QChar('l') }, new int[] { 1, 4 }));
            }
            else
            {
                QChar[] charArray = new QChar[env.Count()];
                for (int i = 0; i < env.Count(); i++)
                {
                    charArray[i] = new QChar(env[i]);
                }

                result.Add(new QCharNDimArray(charArray, new int[] { 1, env.Count() }));
            }

            return result;
        }

        public List<IQLabValue> setenv(List<IQLabValue> args)
        {
            // Check number of arguments
            string name = null, value = null;
            if (args.Count == 1 || args.Count == 2)
            {
                if (Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QCharNDimArray))
                {
                    QCharNDimArray array = new QCharNDimArray(args.ElementAt(0));

                    name = "";
                    foreach (QChar qchar in array.Data)
                    {
                        name += qchar.Real;
                    }
                }
                else
                {
                    throw new NumericalCalculationsException("??? Error using ==> setenv \nFirst argument must be a char array!");
                }

                if (args.Count == 2)
                {
                    if (Helper.canConvertToEnum(args.ElementAt(1), IQLabValueType.QCharNDimArray))
                    {
                        QCharNDimArray array = new QCharNDimArray(args.ElementAt(1));

                        value = "";
                        foreach (QChar qchar in array.Data)
                        {
                            value += qchar.Real;
                        }
                    }
                    else
                    {
                        throw new NumericalCalculationsException("??? Error using ==> setenv \nSecond argument must be a char array!");
                    }
                }
            }
            else
            {
                throw new NumericalCalculationsException("??? Error using ==> setenv \nFunction has two arguments!");
            }

            // Set environment variable
            System.Environment.SetEnvironmentVariable(name, value);

            List<IQLabValue> result = new List<IQLabValue>();
            result.Add(new QBooleanNDimArray(new QBoolean[] { new QBoolean(true) }, new int[] { 1, 1 }));
            return result;
        }

        public List<IQLabValue> tril(List<IQLabValue> args)
        {
            List<IQLabValue> res = new List<IQLabValue>();

            if (!Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QFloat64NDimArray))
            {
                throw new NumericalCalculationsException("??? Error using ==> tril \nFirst argument is not in proper format!");
            }

            int diagonal = 0;

            if (args.Count == 2)
            {
                if (!Helper.canConvertToEnum(args.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                {
                    throw new NumericalCalculationsException("??? Error using ==> tril \nSecond argument is not in proper format!");
                }

                QFloat64NDimArray diagonalArray = new QFloat64NDimArray(args.ElementAt(1));

                diagonal = (int)diagonalArray.Data[0].Real;
            }

            QFloat64NDimArray inputArray = new QFloat64NDimArray(args.ElementAt(0));

            int columnsCount = inputArray.DimensionSize[0];
            int rowsCount = inputArray.DimensionSize[1];

            if (columnsCount != rowsCount)
            {
                throw new NumericalCalculationsException("??? Error using ==> tril \nNot square matrix!");
            }

            QFloat64NDimArray resultArray = new QFloat64NDimArray(inputArray.DimensionSize);
            int limit = 0;

            for (int i = 0; i < rowsCount; i++)
            {
                for (int j = 0; j < columnsCount; j++)
                {
                    if (limit + diagonal >= i * rowsCount + j)
                    {
                        resultArray[i, j] = new QFloat64(inputArray[i, j]);
                    }
                    else
                    {
                        resultArray[i, j] = new QFloat64(0);
                    }
                }

                limit += rowsCount + 1;
            }

            List<IQLabValue> result = new List<IQLabValue>();
            result.Add(resultArray);

            return result;
        }

        public List<IQLabValue> triu(List<IQLabValue> args)
        {
            List<IQLabValue> res = new List<IQLabValue>();

            if (!Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QFloat64NDimArray))
            {
                throw new NumericalCalculationsException("??? Error using ==> triu \nFirst argument is not in proper format!");
            }

            int diagonal = 0;

            if (args.Count == 2)
            {
                if (!Helper.canConvertToEnum(args.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                {
                    throw new NumericalCalculationsException("??? Error using ==> tril \nSecond argument is not in proper format!");
                }

                QFloat64NDimArray diagonalArray = new QFloat64NDimArray(args.ElementAt(1));

                diagonal = (int)diagonalArray.Data[0].Real;
            }

            QFloat64NDimArray inputArray = new QFloat64NDimArray(args.ElementAt(0));

            int columnsCount = inputArray.DimensionSize[0];
            int rowsCount = inputArray.DimensionSize[1];

            if (columnsCount != rowsCount)
            {
                throw new NumericalCalculationsException("??? Error using ==> tril \nNot square matrix!");
            }

            QFloat64NDimArray resultArray = new QFloat64NDimArray(inputArray.DimensionSize);

            int limit = 0;

            for (int i = 0; i < rowsCount; i++)
            {
                for (int j = 0; j < columnsCount; j++)
                {
                    if (limit + diagonal <= i * rowsCount + j)
                    {
                        resultArray[i, j] = new QFloat64(inputArray[i, j]);
                    }
                    else
                    {
                        resultArray[i, j] = new QFloat64(0);
                    }
                }

                limit += rowsCount + 1;
            }

            List<IQLabValue> result = new List<IQLabValue>();
            result.Add(new QFloat64NDimArray(resultArray));

            return result;
        }

        public List<IQLabValue> diag(List<IQLabValue> args)
        {
            if (args.Count <= 0 || args.Count > 2)
            {
                throw new NumericalCalculationsException("??? Error using ==> diag \nOne or two arguments are needed!");
            }

            QFloat64NDimArray input = new QFloat64NDimArray(args.ElementAt(0));
            int size = input.DimensionSize[0] > input.DimensionSize[1] ? input.DimensionSize[0] : input.DimensionSize[1];
            QFloat64NDimArray resultArray = null;

            if (args.Count == 1)
            {
                if (!Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QFloat64NDimArray))
                {
                    throw new NumericalCalculationsException("??? Error using ==> ikag \nFirst argument is not in proper format!");
                }

                if (input.Dimension == 2 && input.DimensionSize[0] == input.DimensionSize[1])
                {
                    resultArray = new QFloat64NDimArray(new int[] { 1, size });

                    for (int i = 0; i < size; i++)
                    {
                        resultArray.SetDataElement(i, input[i, i]); ;
                    }
                }
                else if (input.Dimension == 2 && (input.DimensionSize[0] == 1 || input.DimensionSize[1] == 1))
                {
                    resultArray = new QFloat64NDimArray(new int[] { size, size });

                    for (int i = 0; i < size; i++)
                    {
                        for (int j = 0; j < size; j++)
                        {
                            if (i == j)
                            {
                                resultArray[i, j] = new QFloat64(input.Data[i]);
                            }
                            else
                            {
                                resultArray[i, j] = new QFloat64(0);
                            }
                        }
                    }
                }
                else
                {
                    throw new NumericalCalculationsException("??? Error using ==> diag \nBad dimension of matrix!");
                }

            }
            else if (args.Count == 2)
            {
                if (!Helper.canConvertToEnum(args.ElementAt(0), IQLabValueType.QFloat64NDimArray) || !Helper.canConvertToEnum(args.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                {
                    throw new NumericalCalculationsException("??? Error using ==> diag \nArguments are not in proper format!");
                }

                QFloat64NDimArray kArray = new QFloat64NDimArray(args.ElementAt(1));
                int k = (int)kArray.Data[0].Real;

                if (input.Dimension == 2 && input.DimensionSize[0] == input.DimensionSize[1] && input.DimensionSize[0] > 1)
                {
                    if (size - Math.Abs(k) > 0)
                    {
                        resultArray = new QFloat64NDimArray(new int[] { 1, size - Math.Abs(k) });

                        int p = 0;
                        for (int i = 0; i < size; i++)
                        {
                            for (int j = 0; j < size; j++)
                            {
                                if (j - i == k)
                                {
                                    resultArray.SetDataElement(p, input[i, j]);
                                    p++;
                                }
                            }
                        }
                    }
                    else
                    {
                        resultArray = new QFloat64NDimArray(new QFloat64[] { new QFloat64(0) }, new int[] { 1, 1 });
                    }
                }
                else if (input.Dimension == 2 && (input.DimensionSize[0] == 1 || input.DimensionSize[1] == 1))
                {
                    resultArray = new QFloat64NDimArray(new int[] { size + Math.Abs(k), size + Math.Abs(k) });

                    int p = 0;
                    for (int i = 0; i < size + Math.Abs(k); i++)
                    {
                        for (int j = 0; j < size + Math.Abs(k); j++)
                        {
                            if (j - i == k)
                            {
                                resultArray[i, j] = new QFloat64(input[0, p]);
                                p++;
                            }
                            else
                            {
                                resultArray[i, j] = new QFloat64(0);
                            }
                        }
                    }
                }
                else
                {
                    throw new NumericalCalculationsException("??? Error using ==> diag \nBad dimension of matrix!");
                }
            }
            else
            {
                throw new NumericalCalculationsException("??? Error using ==> diag \nToo much arguments!");
            }

            List<IQLabValue> result = new List<IQLabValue>();
            result.Add(resultArray);

            return result;
        }
    }
}
